<template>
  <div>
    <Form
      ref="formRef"
      :model="formModel"
      :layout="getProps?.layout"
      :label-col="getProps?.labelCol"
      :wrapper-col="getProps?.wrapperCol"
      :labelAlign="getProps?.labelAlign"
      @keypress.enter="handleEnterPress"
    >
      <Row v-bind="getRow">
        <template v-for="schema in getSchemas" :key="schema.field">
          <Col
            :span="schema.colProps?.span"
            v-show="getIsShow(schema, formModel[schema.field])"
            v-if="getIfShow(schema, formModel[schema.field])"
          >
            <template v-if="showComponent(schema)">
              <SimpleFormItem
                :refreshFieldObj="refreshFieldObj"
                :schema="schema"
                :form-api="formApi"
                :isWorkFlow="isWorkFlow"
                v-model:value="formModel[schema.field]"
              />
            </template>
          </Col>
        </template>
      </Row>

      <div :style="{ textAlign: getProps.buttonLocation }">
        <slot name="buttonBefore"></slot>

        <a-button type="primary" v-if="getProps.showSubmitButton" @click="handleSubmit">
          {{ t('提交') }}
        </a-button>
        <a-button style="margin-left: 10px" v-if="getProps.showResetButton" @click="handleReset">
          {{ t('重置') }}
        </a-button>
        <slot name="buttonAfter"></slot>
      </div>
    </Form>
  </div>
</template>
<script lang="ts" setup>
  import { Form, FormInstance, Row, Col, Modal, message } from 'ant-design-vue';
  import {
    cloneDeep,
    isArray,
    isBoolean,
    isFunction,
    isObject,
    isString,
    uniqBy,
    isNil,
  } from 'lodash-es';
  import {
    computed,
    reactive,
    ref,
    provide,
    unref,
    nextTick,
    toRaw,
    createVNode,
    inject,
    onMounted,
  } from 'vue';
  import {
    CardComponentProps,
    FormActionType,
    FormProps,
    FormSchema,
    GridComponentProps,
    TabComponentProps,
    regTestProps,
    requestProps,
  } from '../../Form/src/types/form';
  import SimpleFormItem from './components/SimpleFormItem.vue';
  import { NamePath, ValidateOptions } from 'ant-design-vue/lib/form/interface';
  import {
    arrayValueComponents,
    defaultValueComponents,
    staticDataComponents,
    noFieldComponent,
    noDefaultValueComponents,
    noShowWorkFlowComponents,
    noShowGenerateComponents,
  } from '../../Form/src/helper';
  import { deepMerge } from '/@/utils';
  import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
  import type { ModalFuncProps } from 'ant-design-vue/lib/modal/Modal';
  import { defHttp } from '/@/utils/http/axios';
  import { useI18n } from '/@/hooks/web/useI18n';
  import { needDicDefaultValue } from '../../Designer/src/types';
  import { useMessage } from '/@/hooks/web/useMessage';
  const { t } = useI18n();
  const { notification } = useMessage();
  const formRef = ref<FormInstance>();

  const propsRef = ref<Partial<FormProps>>({});

  const schemaRef = ref<Nullable<FormSchema[]>>(null);

  const emit = defineEmits(['submit']);

  const props = defineProps({
    // 表单配置规则
    formProps: {
      type: Object as PropType<FormProps>,
    },
    //表单数据
    formModel: {
      type: Object as PropType<Recordable>,
      default: () => {},
    },
    //是否是工作流
    isWorkFlow: {
      type: Boolean,
      default: false,
    },
  });

  const getSchemas = computed<FormSchema[]>(() => {
    return (unref(getProps).schemas as any) || unref(schemaRef);
  });

  // Get the basic configuration of the form
  const getProps = computed((): FormProps => {
    return { ...(props.formProps as FormProps), ...unref(propsRef) } as FormProps;
  });

  // Get uniform row style and Row configuration for the entire form
  const getRow = computed((): Recordable => {
    const { baseRowStyle = {}, rowProps } = props.formProps!;

    return {
      style: baseRowStyle,
      ...rowProps,
    };
  });

  onMounted(() => {
    nextTick(() => {
      //添加隐藏组件
      if (unref(getProps)?.hiddenComponent?.length) {
        unref(getProps)?.hiddenComponent?.forEach((component) => {
          formModel[component.bindField] = component.value;
        });
      }
    });
  });

  function showComponent(schema) {
    return props.isWorkFlow
      ? !noShowWorkFlowComponents.includes(schema.type)
      : !noShowGenerateComponents.includes(schema.type);
  }

  function getIsShow(schema: FormSchema, itemValue: any): boolean {
    const { show } = schema;
    const { showAdvancedButton } = getProps.value;
    const itemIsAdvanced = showAdvancedButton
      ? isBoolean(schema.isAdvanced)
        ? schema.isAdvanced
        : true
      : true;

    let isShow = true;

    if (isBoolean(show)) {
      isShow = show;
    }
    if (isFunction(show)) {
      isShow = show({
        values: itemValue,
        model: formModel!,
        schema: schema,
        field: schema.field,
      });
    }

    isShow = isShow && itemIsAdvanced;
    return isShow;
  }

  function getIfShow(schema: FormSchema, itemValue: any): boolean {
    const { ifShow } = schema;

    let isIfShow = true;

    if (isBoolean(ifShow)) {
      isIfShow = ifShow;
    }
    if (isFunction(ifShow)) {
      isIfShow = ifShow({
        values: itemValue,
        model: formModel!,
        schema: schema,
        field: schema.field,
      });
    }
    return isIfShow;
  }

  const formModel = reactive<Recordable>(props.formModel);
  const isCustom = inject<boolean>('isCustomForm', false);
  provide('formModel', formModel);
  provide('formProps', getProps);
  provide('isCustomForm', isCustom || props.isWorkFlow);

  const handleSubmit = async () => {
    try {
      const { submitFunc } = unref(getProps);
      if (submitFunc && isFunction(submitFunc)) {
        await submitFunc();
        return;
      }
      const res = await formRef.value!.validate();
      emit('submit', res);
    } catch (error) {}
  };

  const handleReset = () => {
    resetFields();
  };

  const refreshFieldObj = ref<object>({});

  getComponent(getSchemas.value);

  function getComponent(component) {
    const layoutComponents = ['tab', 'grid', 'card'];
    component?.map((info) => {
      if (layoutComponents.includes(info.type!)) {
        info.children?.map((childInfo) => {
          childInfo.list.map((com) => {
            if (layoutComponents.includes(com.type)) {
              getComponent(childInfo.list);
            } else {
              setComponentDefault(com);
            }
          });
        });
      } else {
        setComponentDefault(info);
      }
    });
  }

  function setComponentDefault(item) {
    if (
      (staticDataComponents.includes(item.component) &&
        (item.componentProps as any)?.datasourceType === 'staticData') ||
      (needDicDefaultValue.includes(item.type) &&
        (item.componentProps as any)?.datasourceType === 'dic')
    ) {
      let { defaultSelect } = item.componentProps as any;
      formModel[item.field] = defaultSelect;
      return;
    }
    let { defaultValue } = item;
    if (!!item.field && !noDefaultValueComponents.includes(item.component)) {
      if (item.component === 'OneForOne') {
        if (item.componentProps.childSchemas?.length) {
          const singleFormObj = {};
          item.componentProps.childSchemas.map((singleCom) => {
            if (
              (staticDataComponents.includes(singleCom.component) &&
                (singleCom.componentProps as any)?.datasourceType === 'staticData') ||
              (needDicDefaultValue.includes(singleCom.type) &&
                (singleCom.componentProps as any)?.datasourceType === 'dic')
            ) {
              let { defaultSelect } = singleCom.componentProps as any;
              singleFormObj[singleCom.field] = defaultSelect;
            } else {
              singleFormObj[singleCom.field] = singleCom.defaultValue;
            }
          });
          formModel[item.field] = [singleFormObj];
        }
        return;
      } else {
        formModel[item.field] = item.component === 'SubForm' ? [] : defaultValue;
        return;
      }
    }
  }

  /**
   * 回车提交表单
   */
  function handleEnterPress(e: KeyboardEvent) {
    const { autoSubmitOnEnter } = unref(getProps);
    if (!autoSubmitOnEnter) return;
    if (e.key === 'Enter' && e.target && e.target instanceof HTMLElement) {
      const target: HTMLElement = e.target as HTMLElement;
      if (target && target.tagName && target.tagName.toUpperCase() == 'INPUT') {
        handleSubmit();
      }
    }
  }

  //调用表单验证
  const validate = async (nameList?: NamePath[]): Promise<any> => formRef.value?.validate(nameList);

  //清除表单验证
  const clearValidate = async (name?: string | string[]): Promise<any> =>
    formRef.value?.clearValidate(name);

  //跳到某个字段
  const scrollToField = async (name: NamePath, options?: ScrollOptions): Promise<any> =>
    formRef.value?.scrollToField(name, options);

  //验证某个字段
  const validateFields = async (
    nameList?: NamePath[] | string,
    options?: ValidateOptions,
  ): Promise<any> => formRef.value?.validateFields(nameList, options);

  const findSchema = (schemaArr, key) => {
    let schema;
    const formListComponent = ['tab', 'grid', 'card'];
    schemaArr?.some((info) => {
      if (formListComponent.includes(info.type!)) {
        const hasComponent = info.children.some((childInfo) => {
          schema = childInfo.list.find((com) => com.field === key);
          if (!!schema) return true;
          schema = findSchema(childInfo.list, key);
          return !!schema;
        });
        return !!hasComponent;
      } else {
        schema = info.field === key ? info : null;
        return !!schema;
      }
    });
    return schema;
  };

  const setDefaultValue = async (): Promise<void> => {
    getComponent(getSchemas.value);
  };

  // 重置表单所有数据
  const resetFields = async (): Promise<void> => {
    Object.keys(formModel).forEach((key) => {
      //没有绑定字段的组件不处理
      if (!key) return;
      const schema = findSchema(unref(getSchemas), key);

      const isInput = schema?.component && defaultValueComponents.includes(schema.component);
      const isSubForm = schema?.component && arrayValueComponents.includes(schema.component);
      const isRange = schema?.component && schema?.component.includes('Range');
      const isStatic =
        schema?.component &&
        staticDataComponents.includes(schema.component) &&
        schema?.componentProps.datasourceType === 'staticData';
      const isDic =
        schema?.type &&
        needDicDefaultValue.includes(schema.type) &&
        schema?.componentProps.datasourceType === 'dic';

      if (isSubForm) {
        if (schema.component === 'OneForOne') {
          if (schema.componentProps.childSchemas?.length) {
            const singleFormObj = {};
            schema.componentProps.childSchemas.map((singleCom) => {
              if (
                (staticDataComponents.includes(singleCom.component) &&
                  singleCom?.componentProps.datasourceType === 'staticData') ||
                (needDicDefaultValue.includes(singleCom.type) &&
                  singleCom?.componentProps.datasourceType === 'dic')
              ) {
                singleFormObj[singleCom.field] = singleCom?.componentProps.defaultSelect;
              } else {
                singleFormObj[singleCom.field] = singleCom.defaultValue;
              }
            });
            formModel[key] = [singleFormObj];
          }
        } else {
          formModel[key] = [];
        }
      } else if (isInput) {
        formModel[key] = schema?.defaultValue || '';
      } else if (isRange) {
        const startTimeKey = key.split(',')[0];
        const endTimeKey = key.split(',')[1];
        formModel[key] = schema?.defaultValue || [];
        formModel[startTimeKey] =
          schema?.defaultValue && schema?.defaultValue.length > 0 && schema?.defaultValue[0];
        formModel[endTimeKey] =
          schema?.defaultValue && schema?.defaultValue.length > 0 && schema?.defaultValue[1];
      } else if (isStatic || isDic) {
        formModel[key] = schema?.componentProps.defaultSelect;
      } else {
        formModel[key] =
          schema?.defaultValue || schema?.defaultValue === 0 ? schema?.defaultValue : '';
      }
    });
    nextTick(() => clearValidate());
  };

  // 更改formProps
  const setProps = async (formProps: Partial<FormProps>): Promise<void> => {
    propsRef.value = deepMerge(unref(propsRef) || {}, formProps);
  };

  //设定某个字段值  慎用  建议直接页面直接操作formModel数据
  const setFieldsValue = async (values: Recordable): Promise<void> => {
    Object.keys(values).forEach((key) => {
      if (!isNil(key)) {
        formModel[key] = values[key];
      }
    });

    executeEvent(getSchemas.value);
  };

  /**
   * 执行schema中 所有change事件
   * 修改bug #5090
   * 为了保证表单赋值触发所有组件的change事件
   */
  const executeEvent = (allSchemas: FormSchema[]) => {
    for (const schema of allSchemas) {
      //如果是这几个组件 需要查询子级
      if (['Card', 'Tab', 'Grid'].includes(schema.component)) {
        if (schema.component === 'Tab') {
          for (const child of schema.children as TabComponentProps[]) {
            executeEvent(child.list);
          }
        }

        if (schema.component === 'Card') {
          for (const child of schema.children as CardComponentProps[]) {
            executeEvent(child.list);
          }
        }

        if (schema.component === 'Grid') {
          for (const child of schema.children as GridComponentProps[]) {
            executeEvent(child.list);
          }
        }
      } else {
        if (schema.componentProps?.['events']) {
          for (const eventKey in schema.componentProps['events']) {
            if (eventKey !== 'change') return;
            try {
              const event = new Function(
                'schema',
                'formModel',
                'formActionType',
                `${schema.componentProps['events'][eventKey]}`,
              );
              event(schema, formModel, formApi);
            } catch (error) {
              console.log('error', error);
              notification.error({
                message: 'Tip',
                description: '触发事件填写有误！',
              });
            }
          }
        }
      }
    }

    // if (componentProps['events']) {
    //   for (const eventKey in componentProps['events']) {
    //     try {
    //       const event = new Function(
    //         'schema',
    //         'formModel',
    //         'formActionType',
    //         `${componentProps['events'][eventKey]}`,
    //       );
    //       componentProps['on' + upperFirst(eventKey)] = function () {
    //         event(props.schema, formModel, props.formApi);
    //       };
    //     } catch (error) {
    //       console.log('error', error);
    //       notification.error({
    //         message: 'Tip',
    //         description: '触发事件填写有误！',
    //       });
    //     }
    //   }
    // }
  };

  //获取表单值  慎用  建议直接页面直接操作formModel数据
  const getFieldsValue = (): Recordable => {
    return toRaw(unref(formModel));
  };

  //更新schema
  const updateSchema = async (data: Partial<FormSchema> | Partial<FormSchema>[]): Promise<void> => {
    let updateData: Partial<FormSchema>[] = [];
    if (isObject(data)) {
      updateData.push(data as FormSchema);
    }
    if (isArray(data)) {
      updateData = [...data];
    }
    const hasField = updateData.every(
      (item) =>
        noFieldComponent.includes(item.component!) || (Reflect.has(item, 'field') && item.field),
    );
    if (!hasField) {
      throw new Error(
        'All children of the form Schema array that need to be updated must contain the `field` field',
      );
    }
    const schema: FormSchema[] = [];
    updateData.forEach((item: FormSchema) => {
      unref(getSchemas).forEach((val: FormSchema) => {
        if (val.key && item.key && val.key === item.key) {
          const newSchema = deepMerge(val, item);
          schema.push(newSchema as FormSchema);
        } else if (val.field === item.field) {
          const newSchema = deepMerge(val, item);
          schema.push(newSchema as FormSchema);
        } else {
          schema.push(val);
        }
      });
    });

    schemaRef.value = uniqBy(schema, 'field');
  };
  //重置schema
  const resetSchema = async (data: Partial<FormSchema> | Partial<FormSchema>[]): Promise<void> => {
    let updateData: Partial<FormSchema>[] = [];
    if (isObject(data)) {
      updateData.push(data as FormSchema);
    }
    if (isArray(data)) {
      updateData = [...data];
    }

    const hasField = updateData.every(
      (item) =>
        noFieldComponent.includes(item.component!) || (Reflect.has(item, 'field') && item.field),
    );
    if (!hasField) {
      throw new Error(
        'All children of the form Schema array that need to be updated must contain the `field` field',
      );
      return;
    }
    schemaRef.value = updateData as FormSchema[];
  };
  //移除schema
  const removeSchemaByFiled = async (fields: string | string[]): Promise<void> => {
    const schemaList: FormSchema[] = cloneDeep(unref(getSchemas));
    if (!fields) {
      return;
    }

    let fieldList: string[] = isString(fields) ? [fields] : fields;
    if (isString(fields)) {
      fieldList = [fields];
    }
    for (const field of fieldList) {
      if (isString(field)) {
        const index = schemaList.findIndex((schema) => schema.field === field);
        if (index !== -1) {
          delete formModel[field];
          schemaList.splice(index, 1);
        }
      }
    }
    schemaRef.value = schemaList;
  };
  //追加schema
  const appendSchemaByField = async (
    schema: FormSchema,
    prefixField: string | undefined,
    first?: boolean | undefined,
  ): Promise<void> => {
    const schemaList: FormSchema[] = cloneDeep(unref(getSchemas));

    const index = schemaList.findIndex((schema) => schema.field === prefixField);
    if (!prefixField || index === -1 || first) {
      first ? schemaList.unshift(schema) : schemaList.push(schema);
      schemaRef.value = schemaList;
      return;
    }
    if (index !== -1) {
      schemaList.splice(index + 1, 0, schema);
    }

    schemaRef.value = schemaList;
  };

  // 提交
  const submit = async (e?: Event): Promise<void> => {
    e && e.preventDefault();
    const { submitFunc } = unref(getProps);
    if (submitFunc && isFunction(submitFunc)) {
      await submitFunc();
      return;
    }
    const formEl = unref(formRef);
    if (!formEl) return;
    try {
      const values = await validate();
      emit('submit', values);
    } catch (error: any) {
      throw new Error(error);
    }
  };

  const showModal = async (modal: ModalFuncProps) => {
    Modal.confirm({
      title: modal.title,
      icon: createVNode(ExclamationCircleOutlined),
      content: createVNode('div', { style: 'color:red;' }, modal.content),
      onOk: modal.onOk,
      onCancel: modal.onCancel,
      ...modal,
    });
  };

  const regTest = async (regular: regTestProps) => {
    const regExpression = regular.regExpression;
    const testRes = regExpression.test(regular.testValue);
    testRes ? message.success(regular.successMessage) : message.error(regular.errorMessage);
  };

  const httpRequest = async (request: requestProps) => {
    if (request.requestType.toLowerCase() === 'get') {
      return defHttp[request.requestType](
        {
          url: request.requestUrl,
          params: request.params,
        },
        {
          errorMessageMode: request.errorMessageMode || 'none',
        },
      );
    } else {
      return defHttp[request.requestType](
        {
          url: request.requestUrl,
          data: request.params,
        },
        {
          errorMessageMode: request.errorMessageMode || 'none',
        },
      );
    }
  };

  const refreshAPI = (field: string) => {
    if (!field) return;
    if (!Object.keys(unref(refreshFieldObj)).includes(field)) {
      unref(refreshFieldObj)[field] = 0;
    }
    unref(refreshFieldObj)[field]++;
  };

  const changeStyle = (schema, style, field?) => {
    if (field) {
      const changeSchema = unref(getSchemas).filter((item) => {
        return item.field === field;
      });
      schema = changeSchema[0];
    }
    schema.componentProps.style = { ...schema.componentProps.style, ...style };
  };

  const formApi: FormActionType = {
    submit,
    validate,
    clearValidate,
    scrollToField,
    validateFields,
    resetFields,
    setProps,
    updateSchema,
    setFieldsValue,
    getFieldsValue,
    removeSchemaByFiled,
    appendSchemaByField,
    resetSchema,
    showModal,
    regTest,
    httpRequest,
    refreshAPI,
    changeStyle,
    setDefaultValue,
  };

  //将表单方法  导出 给父组件使用。
  defineExpose<FormActionType>(formApi);
</script>
